home *** CD-ROM | disk | FTP | other *** search
/ MacFormat 1997 January / macformat46.iso / Shareware Plus / Developers / Library / Grant's CGI Framework / Grant's CGI Framework / grantscgi / Util / LogUtil.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-09-20  |  13.2 KB  |  603 lines

  1. /*****
  2.  *
  3.  *    LogUtil.c
  4.  *
  5.  *    Logging functions
  6.  *
  7.  *    This is a support file for "Grant's CGI Framework".
  8.  *    Please see the license agreement that accompanies the distribution package
  9.  *    for licensing details.
  10.  *
  11.  *    Copyright ©1995,1996 by Grant Neufeld
  12.  *    grant@acm.com
  13.  *    http://arpp.carleton.ca/cgi/framework/
  14.  *
  15.  *****/
  16.  
  17. #include "MyConfiguration.h"
  18.  
  19. #if kCompileWithLogSupport && !kCompileWithProcessFileSpec
  20. #error "Logging requires the kCompileWithProcessFileSpec to be set on in MyConfiguration.h"
  21. #endif
  22.  
  23. #include <stdio.h>
  24. #include <string.h>
  25. #if kCompilingForWSAPI
  26. #include <WSAPI.h>
  27. #endif
  28.  
  29. #include "constants.h"
  30. #include "globals.h"
  31.  
  32. #include "CGI.h"
  33. #include "DebugUtil.h"
  34. #include "FileUtil.h"
  35.  
  36. #include "LogUtil.h"
  37.  
  38.  
  39. /***  LOCAL VARIABLES  ***/
  40.  
  41. #if kCompileWithLogSupport && !kCompilingForWSAPI
  42. static OSErr    vLogInitialized = false;    /* logging has been initialized */
  43. static OSErr    vLogFileError   = true;        /* returned if LogStartup fails - logging can't be done */
  44. static FSSpec    vLogFileSpec;                /* the location of the log file */
  45. static short    vLogFileRef     = nil;        /* the reference of the open log file - nil if closed */
  46. static Boolean    vLogNeedsFlush  = false;    /* there is data that has been written but not flushed */
  47. #endif
  48.  
  49. /***  LOCAL CONSTANTS  ***/
  50.  
  51. #define krLogFileCreatorType        10002
  52. /* default to SimpleText/TeachText if creator type resource not available */
  53. #define kLogFileDefaultCreatorType    'ttxt'
  54.  
  55.  
  56. /***  LOCAL MACROS  ***/
  57.  
  58. #define logFileEnsureOpen()    ((vLogFileRef == nil)? LogFileOpen() : noErr)
  59.  
  60.  
  61. /***  LOCAL PROTOTYPES  ***/
  62.  
  63. /***  FUNCTIONS  ***/
  64.  
  65. #if kCompileWithLogSupport && !kCompilingForWSAPI
  66.  
  67. /* Initialize the log file. */
  68. OSErr
  69. LogStartup ( void )
  70. {
  71.     Str255        logFileName;
  72.     Handle        logFileCreatorHdl;    /* the resource containing the creator type */
  73.     OSType        creatorType;        /* used when creating new log file */
  74.     Boolean        isFolder;            /* used for Finder alias resolving */
  75.     Boolean        wasAliased;            /* used for Finder alias resolving */
  76.     
  77.     vLogInitialized  = true;
  78.     vLogFileError    = noErr;
  79.     vLogFileRef      = nil;
  80.     logFileName[nil] = nil;
  81.     vLogNeedsFlush   = false;
  82.     
  83.     /* get the name of the log file */
  84.     GetIndString ( logFileName, krFilenamesStrs, kriFilenameLog );
  85.     if ( logFileName[nil] == nil )
  86.     {
  87.         /* string of file name is invalid */
  88.         vLogFileError = ResError ();
  89.     }
  90.     
  91.     /* build the FSSpec */
  92.     if ( vLogFileError == noErr )
  93.     {
  94.         vLogFileError = FSMakeFSSpec ( gProcessFSSpec.vRefNum, gProcessFSSpec.parID,
  95.             logFileName, &vLogFileSpec );
  96.     }
  97.     
  98.     if ( vLogFileError == fnfErr )
  99.     {
  100.         /* determine the creator type of the log file */
  101.         logFileCreatorHdl = GetResource ( 'TNAM', krLogFileCreatorType );
  102.         if ( logFileCreatorHdl != NULL )
  103.         {
  104.             creatorType = **((OSType **)logFileCreatorHdl);
  105.             ReleaseResource ( logFileCreatorHdl );
  106.         }
  107.         else
  108.         {
  109.             /* use default if creator type resource not found */
  110.             creatorType = kLogFileDefaultCreatorType;
  111.         }
  112.         vLogFileError = FSpCreate ( &vLogFileSpec, creatorType, kTypeText, smSystemScript );
  113.     }
  114.     else if ( vLogFileError == noErr )
  115.     {
  116.     /* resolve if the file is an alias */
  117.         vLogFileError = ResolveAliasFileMountOption ( &vLogFileSpec, true, &isFolder, &wasAliased, false );
  118.     }
  119.     
  120.     if ( vLogFileError == noErr )
  121.     {
  122.         vLogFileError = LogFileOpen ();
  123.     }
  124.     
  125.     #if kCompileWithDebugLogging
  126.     if ( vLogFileError == noErr )
  127.     {
  128.         LogStringAndSeparatorP    ( gProcessFSSpec.name, ' ' );
  129.         LogStringBreakP            ( gVersionStr );
  130.     }
  131.     #endif
  132.     
  133.     return vLogFileError;
  134. } /* LogStartup */
  135.  
  136.  
  137. /*  */
  138. OSErr
  139. LogQuit ( void )
  140. {
  141.     OSErr    theErr;
  142.     
  143.     if ( (vLogFileError == noErr) && (vLogFileRef != nil) )
  144.     {
  145.         theErr = LogFileClose ();
  146.     }
  147.     else
  148.     {
  149.         theErr = noErr;
  150.     }
  151.     
  152.     return theErr;
  153. } /* LogQuit */
  154.  
  155.  
  156. #pragma mark -
  157.  
  158. /* Open the log file for writing. Writing will begin at the end of the file (appending) */
  159. OSErr
  160. LogFileOpen ( void )
  161. {
  162.     OSErr    theErr;
  163.     
  164.     if ( vLogFileError == noErr )
  165.     {
  166.         my_assert ( vLogFileRef == nil, "\pLogFileOpen: vLogFileRef is not nil - file already open" );
  167.         
  168.         /* open the data fork for writing */
  169.         theErr = FSpOpenDF ( &vLogFileSpec, fsWrPerm, &vLogFileRef );
  170.         if ( theErr == noErr )
  171.         {
  172.             /* set the file position to the end of the file */
  173.             theErr = SetFPos ( vLogFileRef, fsFromLEOF, nil );
  174.         }
  175.         else
  176.         {
  177.             /* there was an error */
  178.             vLogFileRef = nil;
  179.         }
  180.     }
  181.     else
  182.     {
  183.         theErr = vLogFileError;
  184.     }
  185.     
  186.     return theErr;
  187. } /* LogFileOpen */
  188.  
  189.  
  190. /* Close the log file. */
  191. OSErr
  192. LogFileClose ( void )
  193. {
  194.     OSErr    theErr;
  195.     
  196.     if ( vLogFileError == noErr )
  197.     {
  198.         my_assert ( vLogFileRef != nil, "\pLogFileClose: vLogFileRef is nil" );
  199.         
  200.         theErr = FSClose ( vLogFileRef );
  201.         vLogFileRef = nil;
  202.     }
  203.     else
  204.     {
  205.         theErr = vLogFileError;
  206.     }
  207.     
  208.     return theErr;
  209. } /* LogFileClose */
  210.  
  211.  
  212. /* ••• need to upgrade this to just flush the data to disk - but I'm in a rush to
  213.     get this at least working...
  214.     This function should be called periodically after data has been written to the
  215.     log file. It helps to ensure that the data is not lost if there is a crash before
  216.     the file is closed. */
  217. p_export
  218. OSErr
  219. LogFileFlush ( void )
  220. {
  221.     OSErr    theErr;
  222.     
  223.     if ( vLogNeedsFlush )
  224.     {
  225.         theErr = LogFileClose ();
  226.         theErr = LogFileOpen ();
  227.         vLogNeedsFlush = false;
  228.     }
  229.     else
  230.     {
  231.         theErr = noErr;
  232.     }
  233.     
  234.     return theErr;
  235. } /* LogFileFlush */
  236.  
  237. #endif /* kCompileWithLogSupport && !kCompilingForWSAPI */
  238.  
  239.  
  240. #pragma mark -
  241.  
  242. #if kCompileWithLogSupport || kCompilingForWSAPI
  243.  
  244. /* Takes a C format (null terminated) string and writes it to the log file.
  245.     Appends a linebreak after the entry. */
  246. p_export
  247. SInt32
  248. LogWriteEntry ( CGIHdl theCGIHdl, char *theString )
  249. {
  250.     #if kCompilingForWSAPI
  251.     WSAPI_ErrorCode    theErr;
  252.     #else /* if not kCompilingForWSAPI */
  253.     #pragma unused(theCGIHdl)
  254.     OSErr    theErr;
  255.     long    stringLength;
  256.     #endif
  257.     
  258.     my_assert ( theString != NULL, "\pLogString: theString is NULL" );
  259.     
  260.     #if kCompilingForWSAPI
  261.     
  262.     theErr = WSAPI_WriteLogEntry ( (*theCGIHdl)->wsapi, kMyCGIName, theString );
  263.     
  264.     #else /* if not kCompilingForWSAPI */
  265.     
  266.     if ( !vLogInitialized )
  267.     {
  268.         return noErr;
  269.     }
  270.     
  271.     if ( (vLogFileError == noErr) && (theString[nil] != nil) )
  272.     {
  273.         theErr = logFileEnsureOpen ();
  274.         if ( theErr == noErr )
  275.         {
  276.             /* write the line + end null byte to the file */
  277.             stringLength = strlen ( theString );
  278.             theErr = FSWrite ( vLogFileRef, &stringLength, theString );
  279.             if ( theErr == noErr )
  280.             {
  281.                 vLogNeedsFlush = true;
  282.             }
  283.             
  284.             /* write the newline char to the file */
  285.             stringLength = 1;
  286.             theErr = FSWrite ( vLogFileRef, &stringLength, CRSTR );
  287.             if ( theErr == noErr )
  288.             {
  289.                 vLogNeedsFlush = true;
  290.             }
  291.         }
  292.     }
  293.     else
  294.     {
  295.         theErr = vLogFileError;
  296.     }
  297.     
  298.     #endif /* kCompilingForWSAPI */
  299.     
  300.     return theErr;
  301. } /* LogWriteEntry */
  302.  
  303.  
  304. /* This call should generally be used in the form:
  305.     LogWriteDebugEntry ( theCGIHdl, "some message", __FILE__, __LINE__ ); */
  306. p_export
  307. SInt32
  308. LogWriteDebugEntry ( CGIHdl theCGIHdl, char *theString, char *theFileString, UInt16 theLineNumber )
  309. {
  310.     SInt32    theErr;
  311.     OSErr    theOSErr;
  312.     char *    logString;
  313.     UInt16    stringLength;
  314.     
  315.     my_assert ( theString != NULL, "\pLogWriteDebugEntry: theString is NULL" );
  316.     my_assert ( theFileString != NULL, "\pLogWriteDebugEntry: theFileString is NULL" );
  317.     
  318.     stringLength = strlen ( theString );
  319.     stringLength += strlen ( theFileString );
  320.     stringLength += 5 /* max len of a 16bit integer */
  321.         + 4 /* extra characters in the final string */
  322.         + 1; /* null terminator */
  323.     
  324.     logString = CGINewPtr ( theCGIHdl, stringLength, &theOSErr );
  325.     if ( logString != NULL )
  326.     {
  327.         sprintf ( logString, "%s(%d): %s", theFileString, theLineNumber, theString );
  328.         theErr = LogWriteEntry ( theCGIHdl, logString );
  329.     }
  330.     else
  331.     {
  332.         theErr = theOSErr;
  333.     }
  334.     
  335.     return theErr;
  336. } /* LogWriteDebugEntry */
  337.  
  338.  
  339. /* Takes a C format (null terminated) string and writes it to the log file.
  340.     Appends a linebreak after the entry. */
  341.     //••••••••
  342.     // This doesn't do anything for CGIs right now - it's really only useful
  343.     // for WSAPI plug-ins so far. Eventually I'd like to have a status window
  344.     // for CGIs, that could display messages.
  345. p_export
  346. SInt32
  347. DisplayString ( CGIHdl theCGIHdl, char *theString )
  348. {
  349.     #if kCompilingForWSAPI
  350.     WSAPI_ErrorCode    theErr;
  351.     #else /* if not kCompilingForWSAPI */
  352.     #pragma unused(theCGIHdl)
  353.     OSErr    theErr;
  354. //    long    stringLength;
  355.     #endif
  356.     
  357.     my_assert ( theString != NULL, "\pDisplayString: theString is NULL" );
  358.     
  359.     #if kCompilingForWSAPI
  360.     
  361.     theErr = WSAPI_DisplayMessage ( (*theCGIHdl)->wsapi, theString );
  362.     
  363.     #else /* if not kCompilingForWSAPI */
  364.     
  365.     //• For now, this is just a stub when compiling as a CGI.
  366.     theErr = noErr;
  367.     
  368.     #endif /* kCompilingForWSAPI */
  369.     
  370.     return theErr;
  371. } /* DisplayString */
  372.  
  373. #endif /* kCompileWithLogSupport || kCompilingForWSAPI */
  374.  
  375.  
  376. /** CGI Only Log **/
  377. #pragma mark -
  378. #if kCompileWithLogSupport && !kCompilingForWSAPI
  379.  
  380. /* Takes a C format string and writes it to the log file.
  381.     Does not append linebreak or any other character so the next write to the log file
  382.     will follow directly after this write. If you want to have theString followed by
  383.     a linebreak, you need to call one of the functions that appends a separator
  384.     character after theString. */
  385. p_export
  386. OSErr
  387. LogString ( const char *theString )
  388. {
  389.     OSErr    theErr;
  390.     long    stringLength;
  391.     
  392.     my_assert ( theString != NULL, "\pLogString: theString is NULL" );
  393.     
  394.     if ( !vLogInitialized )
  395.     {
  396.         return noErr;
  397.     }
  398.     
  399.     if ( (vLogFileError == noErr) && (theString[nil] != nil) )
  400.     {
  401.         theErr = logFileEnsureOpen ();
  402.         if ( theErr == noErr )
  403.         {
  404.             stringLength = strlen ( theString );
  405.             
  406.             /* write the line + end null byte to the file */
  407.             theErr = FSWrite ( vLogFileRef, &stringLength, theString );
  408.             if ( theErr == noErr )
  409.             {
  410.                 vLogNeedsFlush = true;
  411.             }
  412.         }
  413.     }
  414.     else
  415.     {
  416.         theErr = vLogFileError;
  417.     }
  418.     
  419.     return theErr;
  420. } /* LogString */
  421.  
  422.  
  423. /* theString is a Pascal format string (max 255 characters)
  424.     Does not append linebreak or any other character so the next write to the log file
  425.     will follow directly after this write. If you want to have theString followed by
  426.     a linebreak, you need to call one of the functions that appends a separator
  427.     character after theString. */
  428. p_export
  429. OSErr
  430. LogStringP ( const StringPtr theString )
  431. {
  432.     OSErr    theErr;
  433.     long    stringLength;
  434.     
  435.     my_assert ( theString != NULL, "\pLogStringP: theString is NULL" );
  436.     
  437.     if ( !vLogInitialized )
  438.     {
  439.         return noErr;
  440.     }
  441.     
  442.     if ( (vLogFileError == noErr) && (theString[nil] != nil) )
  443.     {
  444.         theErr = logFileEnsureOpen ();
  445.         if ( theErr == noErr )
  446.         {
  447.             stringLength = (unsigned char) (theString[nil]);
  448.             
  449.             /* write the line + end null byte to the file */
  450.             theErr = FSWrite ( vLogFileRef, &stringLength, theString + 1 );
  451.             if ( theErr == noErr )
  452.             {
  453.                 vLogNeedsFlush = true;
  454.             }
  455.         }
  456.     }
  457.     else
  458.     {
  459.         theErr = noErr;
  460.     }
  461.     
  462.     return theErr;
  463. } /* LogStringP */
  464.  
  465.  
  466. /* Takes a C format string and writes it to the log file.
  467.     Appends theSeparator after theString. This may be used for writing theString
  468.     followed by a linefeed, or perhaps a tab character, among other uses. */
  469. p_export
  470. OSErr
  471. LogStringAndSeparator ( const char *theString, char theSeparator )
  472. {
  473.     OSErr    theErr;
  474.     long    stringLength;
  475.     
  476.     my_assert ( theString != NULL, "\pLogStringAndSeparator: theString is NULL" );
  477.     
  478.     if ( !vLogInitialized )
  479.     {
  480.         return noErr;
  481.     }
  482.     
  483.     if ( vLogFileError == noErr )
  484.     {
  485.         theErr = logFileEnsureOpen ();
  486.         if ( theErr == noErr )
  487.         {
  488.             if ( theString[nil] != nil )
  489.             {
  490.                 stringLength = strlen ( theString );
  491.                 
  492.                 /* write the line + end null byte to the file */
  493.                 theErr = FSWrite ( vLogFileRef, &stringLength, theString );
  494.             }
  495.             
  496.             /* write the newline char to the file */
  497.             stringLength = 1;
  498.             theErr = FSWrite ( vLogFileRef, &stringLength, &theSeparator );
  499.             if ( theErr == noErr )
  500.             {
  501.                 vLogNeedsFlush = true;
  502.             }
  503.         }
  504.     }
  505.     else
  506.     {
  507.         theErr = noErr;
  508.     }
  509.     
  510.     return theErr;
  511. } /* LogStringAndSeparator */
  512.  
  513.  
  514. /* Same as 'LogStringAndSeparator', except theString is a Pascal format string
  515.     (max 255 characters) */
  516. p_export
  517. OSErr
  518. LogStringAndSeparatorP ( const StringPtr theString, char theSeparator )
  519. {
  520.     OSErr    theErr;
  521.     long    stringLength;
  522.     
  523.     my_assert ( theString != NULL, "\pLogStringAndSeparatorP: theString is NULL" );
  524.     
  525.     if ( !vLogInitialized )
  526.     {
  527.         return noErr;
  528.     }
  529.     
  530.     if ( vLogFileError == noErr )
  531.     {
  532.         theErr = logFileEnsureOpen ();
  533.         if ( theErr == noErr )
  534.         {
  535.             if ( theString[nil] != nil )
  536.             {
  537.                 stringLength = (unsigned char) (theString[nil]);
  538.                 
  539.                 /* write the line + end null byte to the file */
  540.                 theErr = FSWrite ( vLogFileRef, &stringLength, theString + 1 );
  541.             }
  542.             
  543.             /* write the newline char to the file */
  544.             stringLength = 1;
  545.             theErr = FSWrite ( vLogFileRef, &stringLength, &theSeparator );
  546.             if ( theErr == noErr )
  547.             {
  548.                 vLogNeedsFlush = true;
  549.             }
  550.         }
  551.     }
  552.     else
  553.     {
  554.         theErr = noErr;
  555.     }
  556.     
  557.     return theErr;
  558. } /* LogStringAndSeparatorP */
  559.  
  560.  
  561. /**  DEBUG LOGGING  **/
  562. #pragma mark -
  563. #if kCompileWithDebugLogging
  564.  
  565. /* These functions will prepend "DEBUG: " in the log file before writing theString
  566.     that is passed to them.
  567.     The functions are prototyped in "LogUtil.h" to map to NULL (do nothing) if
  568.     the 'kCompileWithDebugLogging' option is off in "MyConfiguration.h" */
  569.  
  570. /*  */
  571. p_export
  572. OSErr
  573. LogStringDebug ( const char *theString )
  574. {
  575.     OSErr    theErr;
  576.     
  577.     theErr = LogStringP ( "\pDEBUG: " );
  578.     theErr = LogStringAndSeparator ( theString, kLogLinebreak );
  579.     
  580.     return theErr;
  581. } /* LogStringDebug */
  582.  
  583.  
  584. /*  */
  585. p_export
  586. OSErr
  587. LogStringDebugP ( const StringPtr theString )
  588. {
  589.     OSErr    theErr;
  590.     
  591.     theErr = LogStringP ( "\pDEBUG: " );
  592.     theErr = LogStringAndSeparatorP ( theString, kLogLinebreak );
  593.     
  594.     return theErr;
  595. } /* LogStringDebugP */
  596.  
  597. #endif /* kCompileWithDebugLogging */
  598.  
  599. #endif /* kCompileWithLogSupport  && !kCompilingForWSAPI */
  600.  
  601.  
  602. /*****  EOF  *****/
  603.